package org.tech4j.edi.persistence.jdbc;

import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.tech4j.common.core.model.page.PageQuery;
import org.tech4j.common.core.model.page.PageResult;
import org.tech4j.common.utils.EnumUtils;
import org.tech4j.common.utils.JsonUtils;
import org.tech4j.edi.common.entity.RetryBizConfig;
import org.tech4j.edi.common.enums.RetryModelEnum;
import org.tech4j.edi.common.result.EdiResult;
import org.tech4j.edi.persistence.api.RetryConfigPersistService;
import org.tech4j.edi.persistence.api.query.RetryConfigQuery;
import org.tech4j.edi.persistence.jdbc.util.DefaultJdbcListFactory;
import org.tech4j.edi.persistence.jdbc.util.JdbcListFactory;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @program: tech4j
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/8 20:02
 **/
public class JdbcRetryConfigPersistService implements RetryConfigPersistService {

    private static final String RETRY_CONFIG_FIELDS = "biz_type, biz_name, max_retry_times, retry_interval, retry_model, hour_over_times, run_duration, env, extra_field, enabled, remark, version";
    private static final String BASE_FIND_STATEMENT = "select id, " + RETRY_CONFIG_FIELDS
            + " from retry_biz_config";

    private static final String DEFAULT_SELECT_SQL = BASE_FIND_STATEMENT + " where id = ?";
    private static final String DEFAULT_INSERT_STATEMENT = "insert into retry_biz_config(" + RETRY_CONFIG_FIELDS
            + ", id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ";

    private static final String DEFAULT_UPDATE_STATEMENT = "update retry_biz_config " + "set "
            + RETRY_CONFIG_FIELDS.replaceAll(", ", "=?, ") + "=? where id = ?";

    private static final String DEFAULT_DELETE_STATEMENT = "delete from retry_biz_config where id = ?";

    private static final String DEFAULT_UPDATE_STATUS_STATEMENT = "update retry_biz_config set enabled = ? where id = ?";
    private static final String DEFAULT_SELECT_CONFIG_COUNT = "select count(*) from retry_biz_config";
    private String insertRetryConfigSql = DEFAULT_INSERT_STATEMENT;
    private String updateRetryConfigSql = DEFAULT_UPDATE_STATEMENT;
    private String selectRetryConfigSql = DEFAULT_SELECT_SQL;
    private String updateStatusSql = DEFAULT_UPDATE_STATUS_STATEMENT;

    private final JdbcTemplate jdbcTemplate;

    private final JdbcListFactory listFactory;

    private final RowMapper<RetryBizConfig> rowMapper = new RetryBizConfigRowMapper();

    public JdbcRetryConfigPersistService(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(jdbcTemplate));
    }

    @Override
    public EdiResult<String> addRetryConfig(RetryBizConfig retryBizConfig) {
        int update = jdbcTemplate.update(insertRetryConfigSql
                , retryBizConfig.getBizType()
                , retryBizConfig.getBizName()
                , retryBizConfig.getMaxRetryTimes()
                , retryBizConfig.getRetryInterval()
                , retryBizConfig.getRetryModel().getType()
                , retryBizConfig.getHourOverTimes()
                , retryBizConfig.getRunDuration()
                , retryBizConfig.getEnv()
                , JsonUtils.toJsonString(retryBizConfig.getExtraField())
                , retryBizConfig.getEnabled()
                , retryBizConfig.getRemark()
                , retryBizConfig.getVersion()
                , retryBizConfig.getId()
        );
        if (update <= 0) {
            return EdiResult.ofFailed0(500, "新增重试配置失败");
        }
        return EdiResult.ofSuccess(retryBizConfig.getId());
    }

    @Override
    public EdiResult<Void> updateRetryConfig(RetryBizConfig retryBizConfig) {
        int update = jdbcTemplate.update(updateRetryConfigSql
                , retryBizConfig.getBizType()
                , retryBizConfig.getBizName()
                , retryBizConfig.getMaxRetryTimes()
                , retryBizConfig.getRetryInterval()
                , retryBizConfig.getRetryModel().getType()
                , retryBizConfig.getHourOverTimes()
                , retryBizConfig.getRunDuration()
                , retryBizConfig.getEnv()
                , JsonUtils.toJsonString(retryBizConfig.getExtraField())
                , retryBizConfig.getEnabled()
                , retryBizConfig.getRemark()
                , retryBizConfig.getVersion()
                , retryBizConfig.getId()
        );
        if (update <= 0) {
            return EdiResult.ofFailed0(500, "更新重试配置失败");
        }
        return EdiResult.ofSuccess();
    }

    @Override
    public EdiResult<Void> deleteRetryConfig(String id) {
        int delete = jdbcTemplate.update(DEFAULT_DELETE_STATEMENT, id);
        if (delete <= 0) {
            return EdiResult.ofFailed0(500, "删除重试配置失败");
        }
        return EdiResult.ofSuccess();
    }

    @Override
    public EdiResult<RetryBizConfig> getRetryConfigById(String id) {
        return EdiResult.ofSuccess(jdbcTemplate.queryForObject(selectRetryConfigSql, new RetryBizConfigRowMapper(), id));
    }

    @Override
    public EdiResult<RetryBizConfig> getOneRetryConfig(RetryConfigQuery query) {
        StringBuilder stringBuilder = new StringBuilder(BASE_FIND_STATEMENT);
        Map<String, Object> parameters = buildListParamAndSql(null, query, stringBuilder);
        List<RetryBizConfig> retryBizConfigs = listFactory.getList(stringBuilder.toString(), parameters, rowMapper);
        if (CollectionUtils.isNotEmpty(retryBizConfigs)) {
            return EdiResult.ofFailed0(500, "当前重试配置不存在");
        }
        return EdiResult.ofSuccess(retryBizConfigs.get(0));
    }

    @Override
    public EdiResult<Void> startRetryConfig(String id) {
        int start = jdbcTemplate.update(updateStatusSql, true, id);
        if (start <= 0) {
            return EdiResult.ofFailed0(500, "启用重试配置失败");
        }
        return EdiResult.ofSuccess();
    }

    @Override
    public EdiResult<Void> stopRetryConfig(String id) {
        int stop = jdbcTemplate.update(updateStatusSql, false, id);
        if (stop <= 0) {
            return EdiResult.ofFailed0(500, "禁用重试配置失败");
        }
        return EdiResult.ofSuccess();
    }

    @Override
    public List<RetryBizConfig> list(RetryConfigQuery query) {
        StringBuilder stringBuilder = new StringBuilder(BASE_FIND_STATEMENT);
        Map<String, Object> parameters = buildListParamAndSql(null, query, stringBuilder);
        return listFactory.getList(stringBuilder.toString(), parameters, rowMapper);
    }

    @Override
    public Long count(RetryConfigQuery query) {
        StringBuilder stringBuilder = new StringBuilder(DEFAULT_SELECT_CONFIG_COUNT);
        Map<String, Object> parameters = buildListParamAndSql(null, query, stringBuilder);
        return listFactory.count(stringBuilder.toString(), parameters);
    }

    @Override
    public PageResult<RetryBizConfig> page(PageQuery page, RetryConfigQuery configQuery) {
        StringBuilder stringBuilder = new StringBuilder(BASE_FIND_STATEMENT);
        Map<String, Object> parameters = buildListParamAndSql(page, configQuery, stringBuilder);
        long count = 0;
        if (Boolean.TRUE.equals(page.getCount())) {
            count = Optional.ofNullable(this.count(configQuery))
                    .orElse(0L);
        }
        List<RetryBizConfig> retryBizConfigList = listFactory.getList(stringBuilder.toString(), parameters, rowMapper);
        if (CollectionUtils.isNotEmpty(retryBizConfigList)) {
            return PageResult.emptyPageResult(page);
        }
        PageResult<RetryBizConfig> pageResult = new PageResult<>();
        pageResult.setPageNum(page.getPageNum());
        pageResult.setPageSize(page.getPageSize());
        pageResult.setRows(retryBizConfigList);
        pageResult.setTotal(count);
        return pageResult;
    }

    private static class RetryBizConfigRowMapper implements RowMapper<RetryBizConfig> {

        @Override
        public RetryBizConfig mapRow(ResultSet rs, int rowNum) throws SQLException {
            RetryBizConfig retryBizConfig = new RetryBizConfig();
            int status = rs.getInt(6);
            retryBizConfig.setId(rs.getString(1));
            retryBizConfig.setBizType(rs.getString(2));
            retryBizConfig.setBizName(rs.getString(3));
            retryBizConfig.setMaxRetryTimes(rs.getInt(4));
            retryBizConfig.setRetryInterval(rs.getInt(5));
            retryBizConfig.setRetryModel(EnumUtils.getBy(RetryModelEnum.class, (statusEnum -> statusEnum.getType() == status)));
            retryBizConfig.setHourOverTimes(rs.getInt(7));
            retryBizConfig.setRunDuration(rs.getInt(8));
            retryBizConfig.setEnv(rs.getString(9));
            retryBizConfig.setCreatedTime(rs.getDate(10));
            retryBizConfig.setModifiedTime(rs.getDate(11));
            retryBizConfig.setRemark(rs.getString(12));
            retryBizConfig.setVersion(rs.getInt(13));
            return retryBizConfig;
        }
    }

    private static Map<String, Object> buildListParamAndSql(PageQuery page, RetryConfigQuery query, StringBuilder stringBuilder) {
        Map<String, Object> parameters = Maps.newHashMap();
        stringBuilder.append(" where 1=1");
        if (StringUtils.isNotEmpty(query.getId())) {
            stringBuilder.append(" and id = :id");
            parameters.put("id", query.getId());
        }
        if (StringUtils.isNotBlank(query.getBizType())) {
            stringBuilder.append(" and biz_type = :bizType");
            parameters.put("bizType", query.getBizType());
        }
        if (StringUtils.isNotBlank(query.getBizName())) {
            stringBuilder.append(" and status like concat('%', :bizName, '%')");
            parameters.put("bizName", query.getBizName());
        }
        if (Objects.nonNull(query.getRetryModel())) {
            stringBuilder.append(" and retry_model = :retryModel");
            parameters.put("retryModel", query.getRetryModel().getType());
        }
        stringBuilder.append(" and enabled = :enabled");
        parameters.put("enabled", query.isEnabled());
        if (page != null) {
            stringBuilder.append(" limit :offset, :limit");
            parameters.put("offset", (page.getPageNum() - 1) * page.getPageSize());
            parameters.put("limit", page.getPageSize());
        }
        return parameters;
    }
}
